use anyhow::Result;
use base64::engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD};
use base64::prelude::*;
use std::io::Read;

use crate::Base64Format;

pub fn process_encode(input: &str, format: Base64Format) -> Result<()> {
    let mut read = get_reader(input)?;
    let mut buf = Vec::new();
    read.read_to_end(&mut buf)?;

    let encoded = match format {
        Base64Format::Standard => STANDARD.encode(buf),
        Base64Format::UrlSafe => URL_SAFE_NO_PAD.encode(buf),
    };

    println!("{}", encoded);

    Ok(())
}

pub fn process_decode(input: &str, format: Base64Format) -> Result<()> {
    let mut read = get_reader(input)?;
    let mut buf = String::new();
    read.read_to_string(&mut buf)?;
    let buf = buf.trim();

    let decoded = match format {
        Base64Format::Standard => STANDARD.decode(buf)?,
        Base64Format::UrlSafe => URL_SAFE_NO_PAD.decode(buf)?,
    };

    let decoded = String::from_utf8(decoded)?;
    print!("{}", decoded);

    Ok(())
}

pub fn get_reader(input: &str) -> Result<Box<dyn Read>> {
    let content: Box<dyn Read> = match input {
        "-" => Box::new(std::io::stdin()),
        _ => Box::new(std::fs::File::open(input)?),
    };
    Ok(content)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_process_encode() {
        let input = "Cargo.toml";
        let format = Base64Format::Standard;
        assert!(process_encode(input, format).is_ok());
    }

    #[test]
    fn test_process_decode() {
        let input = "fixtures/b64.txt";
        let format = Base64Format::UrlSafe;
        process_decode(input, format).unwrap();
    }
}
